package edu.sjtu.context.persist;

//jdk class
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.hp.hpl.jena.util.FileUtils;

//custom class
import edu.sjtu.context.core.OntologyExistException;
import edu.sjtu.context.core.OntologyNotFoundException;
import edu.stanford.smi.protegex.owl.ProtegeOWL;
import edu.stanford.smi.protegex.owl.jena.JenaOWLModel;
import edu.stanford.smi.protegex.owl.model.OWLModel;
import edu.stanford.smi.protegex.owl.model.OWLOntology;
import edu.stanford.smi.protegex.owl.repository.RepositoryManager;
import edu.stanford.smi.protegex.owl.repository.impl.LocalFileRepository;

public class FilePersistManager implements PersistManager {
	/**
	 * A single instance of FilePersistManager
	 */
	//private static FilePersistManager fpm = null;
	/**
	 * Ontology library path
	 */
	private String ontLibPath;
	/**
	 * a list of ontoloty name which is managed by ContextManager
	 */
	private List<String> ontologies = null;
	/**
	 * log instance
	 */
	private static Log log = null;
	/**
	 * static inner class to initialize FilePersistManager
	 * @author Roger
	 *
	 */
	//static class PersistManagerHolder{
		//static volatile PersistManager fpm = new FilePersistManager();
	//}
	/**
	 * Constructor of FilePersistManager
	 * 
	 * @author Roger
	 */
	public FilePersistManager() {
		log = LogFactory.getLog(this.getClass());
		this.ontologies = new ArrayList<String>();
	}

	/**
	 * get the FilePersistManager instance
	 * 
	 * @return if the FilePersistManager is already initialized, return the
	 *         instance, or initialized and return it.
	 * @author Roger
	 */
	//public static PersistManager getInstance() {
		//return PersistManagerHolder.fpm;
	//}

	/**
	 * get the path of the ontology library
	 * 
	 * @return the ontLibPath
	 * @author Roger
	 */
	public String getOntLibPath() {
		return ontLibPath;
	}

	/**
	 * set the path of the ontology library. Generally, user should not use this method.
	 * 
	 * @param ontLibPath
	 * 		the ontLibPath to be set
	 * @author Roger
	 */
	public void setOntLibPath(String ontLibPath) {
		this.ontLibPath = ontLibPath;
	}
	
	/**
	 * to add a ontology to the ontology library.
	 * 
	 * @param ontName
	 *            the name of the ontology to be saved to the ontology library
	 * @param url
	 *            the url where to get the ontology in the local file system.
	 * @throws OntologyExistException
	 *             if the name of the being added ontology is already exist,
	 *             throw a exceition
	 * @author Roger
	 */
	public synchronized void addOntology(String ontName, String url)
			throws OntologyExistException, Exception {
		JenaOWLModel model;
		JenaOWLModel intModel;
		Collection errors = new ArrayList();
		if (this.isOntExist(ontName)) {
			throw new OntologyExistException("The ontology:" + ontName
					+ " is already exist.");
		}
		try{
			model = ProtegeOWL.createJenaOWLModelFromInputStream(new FileInputStream(new File(url)));
			intModel = ProtegeOWL.createJenaOWLModelFromInputStream(new FileInputStream(new File(
					Thread.currentThread().getContextClassLoader().getResource("InternetContext.owl").getFile())));
			RepositoryManager rm = model.getRepositoryManager();
			//add internet ontology to created ontology's repository
		    LocalFileRepository lfRep = new LocalFileRepository(new File(Thread.
		    		currentThread().getContextClassLoader().getResource("InternetContext.owl").getFile()), false);
		    rm.addProjectRepository(lfRep);
		    
		    //add the uri of internet ontology to created imports declaration
		    OWLOntology ont = model.getDefaultOWLOntology();
		    ont.addImports(intModel.getDefaultOWLOntology());
		    
		    //set default namespace of the created ontology
		    model.getNamespaceManager().setDefaultNamespace("http://www.cs.sjtu.edu.cn/" + ontName + "#");
		    //add reference of internet ontology to created ontology's namespace declaration
		    model.getNamespaceManager().setPrefix(intModel.getDefaultOWLOntology().getURI()+"#", "intont");
		    model.save(new File(this.ontLibPath + "\\" + ontName + ".owl").toURI(), FileUtils.langXMLAbbrev, errors);
		}
		catch(Exception e){
			throw new Exception(e.getMessage());
		}
		this.ontologies.add(ontName);
	}

	/**
	 * to get a ontology from the ontology library.
	 * @param ontName
	 * 			the name of the ontology. If the notology is exist, this method will return the ontology, or 
	 * 			it will return null.
	 * @return
	 * 			If the ontology with the specified name exsit return OWLModel represent the specified ontology, 
	 *    		or return null.
	 * @author Roger
	 */
	public JenaOWLModel getOntology(String ontName) {
		if(this.isOntExist(ontName)){
			try{
				return ProtegeOWL.createJenaOWLModelFromURI("file:/" + this.ontLibPath + "/" + ontName + ".owl");
			} catch(Exception e){
				return null;
			}
		}else{
			return null;
		}
	}
	
	/**
	 * remove ontology with the given name from the ontology library.
	 * @param ontName
	 * 		name of the ontology to be removed.
	 * @return
	 * 		if successful return 0, or return -1.
	 * @throws OntologyNotFoundException
	 * 		if the ontology with the given name is not exist, throw an Exception.
	 * @author Roger
	 */
	public synchronized void removeOntology(String ontName)
		throws OntologyNotFoundException{
		if(!this.isOntExist(ontName))
			throw new OntologyNotFoundException();
		
		File ontFile = new File(this.ontLibPath + "\\" + ontName + ".owl");
		File ontRepFile = new File(this.ontLibPath + "\\" + ontName + ".repository");
		if(ontFile.exists() && ontFile.isFile()){
			ontFile.delete();
		}
		if(ontRepFile.exists() && ontRepFile.isFile()){
			ontRepFile.delete();
		}
		
		int loc = this.ontologies.indexOf(ontName);
		if(-1 != loc)
			this.ontologies.remove(loc);
	}
	
	/**
	 * check if the ontology with the given name is already exist
	 * 
	 * @param ontName
	 *            ontology name
	 * @return if the ontology with the given name is already exist then return
	 *         true, or return false
	 * @author Roger
	 */
	public boolean isOntExist(String ontName) {
		for(int i=0; i < this.ontologies.size(); i++){
			if(ontName.equalsIgnoreCase((String)this.ontologies.get(i))){
				return true;
			}
		}
		return false;
	}
	
	/**
	 * get all ontologies from the ontology library
	 * 
	 * @return
	 * 		return an array which contain names of all ontologies
	 * @author Roger
	 */
	public String[] getOntologies(){
		String[] onts = new String[this.ontologies.size()];
		
		int count = 0;
		for(String ont : this.ontologies){
			onts[count] = ont;
			++count;
		}
		
		return onts;
	}
	
	/**
	 * update exsit ontology represent by OWLModel.
	 * @param model
	 * 		the ontology to be updated.
	 * @param ontName
	 * 		the name of the ontology to be update.
	 */
	public void updateOntology(JenaOWLModel model, String ontName){
		Collection errors = new ArrayList();
		model.save(new File(this.ontLibPath + "\\" + ontName + ".owl").toURI(), 
	    		FileUtils.langXMLAbbrev, errors, model.getOntModel());
		
		for(Object obj : errors){
			log.error(obj.toString());
		}
	}
}
